﻿namespace ACCOUNTING.Encryption
{
    using Microsoft.VisualBasic.CompilerServices;
    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Security.Cryptography;

    public class Symmetric
    {
        private const int _BufferSize = 0x800;
        private SymmetricAlgorithm _crypto;
        private Data _data;
        private const string _DefaultIntializationVector = "%1Az=-@qT";
        private byte[] _EncryptedBytes;
        private Data _iv;
        private Data _key;
        private bool _UseDefaultInitializationVector;

        private Symmetric()
        {
        }

        public Symmetric(Provider provider, bool useDefaultInitializationVector = true)
        {
            switch (((int) provider))
            {
                case 0:
                    this._crypto = new DESCryptoServiceProvider();
                    break;

                case 1:
                    this._crypto = new RC2CryptoServiceProvider();
                    break;

                case 2:
                    this._crypto = new RijndaelManaged();
                    break;

                case 3:
                    this._crypto = new TripleDESCryptoServiceProvider();
                    break;
            }
            this.Key = this.RandomKey();
            if (useDefaultInitializationVector)
            {
                this.IntializationVector = new Data("%1Az=-@qT");
            }
            else
            {
                this.IntializationVector = this.RandomInitializationVector();
            }
        }

        public Data Decrypt(Data encryptedData)
        {
            Data data=null;
            try
            {
                MemoryStream stream2 = new MemoryStream(encryptedData.Bytes, 0, encryptedData.Bytes.Length);
                byte[] buffer = new byte[(encryptedData.Bytes.Length - 1) + 1];
                this.ValidateKeyAndIv(false);
                CryptoStream stream = new CryptoStream(stream2, this._crypto.CreateDecryptor(), CryptoStreamMode.Read);
                try
                {
                    stream.Read(buffer, 0, encryptedData.Bytes.Length - 1);
                }
                catch (CryptographicException exception1)
                {
                    ProjectData.SetProjectError(exception1);
                    CryptographicException inner = exception1;
                    throw new CryptographicException("Unable to decrypt data. The provided key may be invalid.", inner);
                    ProjectData.ClearProjectError();
                }
                finally
                {
                    stream.Close();
                }
                data = new Data(buffer);
            }
            catch (Exception exception2)
            {
                ProjectData.SetProjectError(exception2);
                ProjectData.ClearProjectError();
            }
            return data;
        }

        public Data Decrypt(Stream encryptedStream)
        {
            MemoryStream stream2 = new MemoryStream();
            byte[] buffer = new byte[0x801];
            this.ValidateKeyAndIv(false);
            CryptoStream stream = new CryptoStream(encryptedStream, this._crypto.CreateDecryptor(), CryptoStreamMode.Read);
            for (int i = stream.Read(buffer, 0, 0x800); i > 0; i = stream.Read(buffer, 0, 0x800))
            {
                stream2.Write(buffer, 0, i);
            }
            stream.Close();
            stream2.Close();
            return new Data(stream2.ToArray());
        }

        public Data Decrypt(Data encryptedData, Data key)
        {
            this.Key = key;
            return this.Decrypt(encryptedData);
        }

        public Data Decrypt(Stream encryptedStream, Data key)
        {
            this.Key = key;
            return this.Decrypt(encryptedStream);
        }

        public Data Encrypt(Data d)
        {
            MemoryStream stream2 = new MemoryStream();
            this.ValidateKeyAndIv(true);
            CryptoStream stream = new CryptoStream(stream2, this._crypto.CreateEncryptor(), CryptoStreamMode.Write);
            stream.Write(d.Bytes, 0, d.Bytes.Length);
            stream.Close();
            stream2.Close();
            return new Data(stream2.ToArray());
        }

        public Data Encrypt(Stream s)
        {
            MemoryStream stream2 = new MemoryStream();
            byte[] buffer = new byte[0x801];
            this.ValidateKeyAndIv(true);
            CryptoStream stream = new CryptoStream(stream2, this._crypto.CreateEncryptor(), CryptoStreamMode.Write);
            for (int i = s.Read(buffer, 0, 0x800); i > 0; i = s.Read(buffer, 0, 0x800))
            {
                stream.Write(buffer, 0, i);
            }
            stream.Close();
            stream2.Close();
            return new Data(stream2.ToArray());
        }

        public Data Encrypt(Data d, Data key)
        {
            this.Key = key;
            return this.Encrypt(d);
        }

        public Data Encrypt(Stream s, Data key)
        {
            this.Key = key;
            return this.Encrypt(s);
        }

        public Data Encrypt(Stream s, Data key, Data iv)
        {
            this.IntializationVector = iv;
            this.Key = key;
            return this.Encrypt(s);
        }

        public Data RandomInitializationVector()
        {
            this._crypto.GenerateIV();
            return new Data(this._crypto.IV);
        }

        public Data RandomKey()
        {
            this._crypto.GenerateKey();
            return new Data(this._crypto.Key);
        }

        private void ValidateKeyAndIv(bool isEncrypting)
        {
            if (this._key.IsEmpty)
            {
                if (!isEncrypting)
                {
                    throw new CryptographicException("No key was provided for the decryption operation!");
                }
                this._key = this.RandomKey();
            }
            if (this._iv.IsEmpty)
            {
                if (!isEncrypting)
                {
                    throw new CryptographicException("No initialization vector was provided for the decryption operation!");
                }
                this._iv = this.RandomInitializationVector();
            }
            this._crypto.Key = this._key.Bytes;
            this._crypto.IV = this._iv.Bytes;
        }

        public Data IntializationVector
        {
            get
            {
                return this._iv;
            }
            set
            {
                this._iv = value;
                this._iv.MaxBytes = this._crypto.BlockSize / 8;
                this._iv.MinBytes = this._crypto.BlockSize / 8;
            }
        }

        public Data Key
        {
            get
            {
                return this._key;
            }
            set
            {
                this._key = value;
                this._key.MaxBytes = this._crypto.LegalKeySizes[0].MaxSize / 8;
                this._key.MinBytes = this._crypto.LegalKeySizes[0].MinSize / 8;
                this._key.StepBytes = this._crypto.LegalKeySizes[0].SkipSize / 8;
            }
        }

        public int KeySizeBits
        {
            get
            {
                return this._crypto.KeySize;
            }
            set
            {
                this._crypto.KeySize = value;
                this._key.MaxBits = value;
            }
        }

        public int KeySizeBytes
        {
            get
            {
                return (this._crypto.KeySize / 8);
            }
            set
            {
                this._crypto.KeySize = value * 8;
                this._key.MaxBytes = value;
            }
        }

        public enum Provider
        {
            DES,
            RC2,
            Rijndael,
            TripleDES
        }
    }
}

